/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ScannerParser;

/**
 *
 * @author jefoutta
 */
import java.util.*;

public class GDA implements Comparable<GDA>{
    //static LinkedList<GDA> allLeaves = new LinkedList();
    //static LinkedList<GDA> allNodes = new LinkedList();
    public static LinkedList<GDA> pruebas = new LinkedList();
    private LinkedList<GDA> children;
    private GDA parent;
    private GDA superParent;
    private String element;
    private boolean pointer;
    private int size;
    private int childIndex;
    private Temporal temporal;
    private Label labelTrue;
    private Label labelFalse;
    private Label labelNext;
    private static int idcount = 0;
    private int id;
    private String typeOperator;
    private String offset;
    private Symbol symbol;

    public GDA(Symbol tsymbol){
        //System.out.println("se creó un nuevo GDA de uno " + symbol.getName());
        this.symbol = tsymbol;
        this.typeOperator = "location";
        this.element = this.symbol.getName();
        this.pointer = false;
        this.size = 1;
        this.children = new LinkedList();
        this.temporal = null;
        this.superParent = null;
        this.id = idcount;
        idcount++;

    }

    public GDA(Symbol tsymbol, GDA index){
        //System.out.println("se creó un nuevo GDA de uno " + symbol.getName());
        this.symbol = tsymbol;
        this.element = this.symbol.getName();
        if(this.symbol.getType().equals("int")||this.symbol.getType().equals("char")||this.symbol.getType().equals("boolean"))
            this.typeOperator = "array";
        else{
            System.out.println("hay un struct---------------------------------------------------------");
            this.typeOperator = "struct";
        }
        this.pointer = false;
        this.size = 1;
        this.children = new LinkedList();
        this.children.add(index);
        this.temporal = null;
        this.superParent = null;
        this.id = idcount;
        idcount++;
    }

    public GDA(String telement){
        if(telement.equals("true"))
            this.element = "1";
        if(telement.equals("false"))
            this.element = "0";
        this.element = telement;
        this.typeOperator = "value";
        this.size = 1;
        this.pointer = false;
        this.children = new LinkedList();
        this.temporal = null;
        this.superParent = null;
        this.id = idcount;
        idcount++;
    }

    public GDA(String operator, GDA unicOperand){
        if(operator.equals("~"))
            this.typeOperator = "aritop";
        else
            this.typeOperator = "logicop";
        this.element = operator;
        this.children = new LinkedList();
        this.pointer = false;
        this.children.add(unicOperand);
        this.setParentTo(unicOperand);
        this.temporal = null;
        this.superParent = null;

    }

    public GDA(String operator, GDA firstOperand, GDA secondOperand){
        //System.out.println("se creó un nuevo GDA de dos " + operator +" "+firstOperand.element+" "+secondOperand.element);
        if((operator.equals("/"))||(operator.equals("*"))||(operator.equals("+"))||(operator.equals("%"))||(operator.equals("-")))
                this.typeOperator = "aritop";
        else
            if((operator.equals(">"))||(operator.equals(">="))||(operator.equals("<"))||(operator.equals("<="))||(operator.equals("!="))||(operator.equals("==")))
                this.typeOperator = "relop";
            else
                if((operator.equals("&&"))||(operator.equals("||")))
                    this.typeOperator = "logicop";
        this.element = operator;
        this.children = new LinkedList();
        this.pointer = false;
        
        this.children.addLast(firstOperand);
        firstOperand.childIndex = this.children.size() -1;
        this.children.addLast(secondOperand);
        secondOperand.childIndex = this.children.size() - 1;
        this.setParentTo(firstOperand);
        //firstOperand.parents.add(this);
        this.setParentTo(secondOperand);
        this.checkForNextLabel(firstOperand);
        this.checkForNextLabel(secondOperand);
        this.setSuperParent();
        //secondOperand.parents.add(this);
        this.checkAndAssign(firstOperand, secondOperand);
        this.temporal = null;
        this.superParent = null;
        this.id = idcount;
        idcount++;
    }

    public GDA(Symbol tsymbol,LinkedList<GDA> gdaList){
        this.symbol = tsymbol;
        this.element = this.symbol.getName();
        this.children = new LinkedList();
        this.typeOperator = "function";
        for(int i = 0; i < gdaList.size(); i++){
            if(gdaList.get(i) != null)
                this.children.add(gdaList.get(i));
        }
        this.temporal = null;
        this.superParent = null;
    }

    public void setParentTo(GDA child){
        if(this != null){
            child.parent = this;
        }
    }

    public void setSuperParent(){
        for(int i = 0; i < this.children.size(); i++)
            if(this.children.get(i).children.size() > 0){
                for(int j = 0; j < this.children.get(i).children.size(); j++)
                    if((j == (this.children.get(i).children.size() - 1))&&(this.children.get(i).children.get(j).typeOperator.equals("relop"))){
                        this.children.get(i).children.get(j).superParent = this;
                    }
            }
    }

    public void checkForNextLabel(GDA child){
        if(child.typeOperator.equals("logicop") && this.typeOperator.equals("logicop"))
            if(!child.element.equals(this.element)){
                System.out.println("setNextLabel to: "+child.element+" -------------------------------");
                child.labelNext = new Label();
            }
    }

    public void setTrueLabel(Label temp){
        this.labelTrue = temp;
    }

    public void setFalseLabel(Label temp){
        this.labelFalse = temp;
    }

    public void setNextLabel(Label temp){
        this.labelNext = temp;
    }

    public void changeParent(GDA target){
        if(this.parent != null)
            this.parent.children.set(this.childIndex, target);
        else{
        }
    }

    public void checkAndAssign(GDA firstOperand, GDA secondOperand){
        GDA temp = null;
        if(firstOperand.size >= secondOperand.size){
            temp = secondOperand.checkFor(firstOperand);
            if(temp == null){
                if(firstOperand.children.size() > 0){
                    for(int i = 0; i < firstOperand.children.size(); i++)
                        this.checkAndAssign(firstOperand.children.get(i), secondOperand);
                }
            }else{
                firstOperand.changeParent(temp);
                firstOperand = temp;
            }
        }else{
            temp = firstOperand.checkFor(secondOperand);
            if(temp == null){
                if(secondOperand.children.size() > 0){
                    for(int i = 0; i < secondOperand.children.size(); i++)
                        this.checkAndAssign(firstOperand, secondOperand.children.get(i));
                }
            }else{
                secondOperand.changeParent(temp);
                secondOperand = temp;
            }
        }
    }

    public GDA checkFor(GDA target){
        //System.out.println("va a buscar "+target.element);
        if(this.compareTo(target) == 0){
            System.out.println("ya existe: "+ this);
            return this;
        }else{
            int i = 0;
            while(i < this.children.size()){
                GDA temp = this.children.get(i).checkFor(target);
                if(temp != null){
                    return temp;
                }
                i++;
            }
            return null;
        }
    };

    public int compareTo(GDA other) {
        //System.out.println("se va a comparar "+this.element+" vs "+other.element);
        int count = 0;
        if(this.children.size() == 0){
            if(this.element.equals(other.element))
                return 0;
            else
                return 1;
        }else{
                if(this.element.equals(other.element)){
                    if(this.children.size() == other.children.size()){
                        while((count < this.children.size())&&(this.children.get(count).compareTo(other.children.get(count)) == 0)){
                            count++;
                        }
                        if(count == this.children.size())
                            return 0;
                        else
                            return 1;
                    }else{
                        return 1;
                    }
                }else{
                    return 1;
                }
        }
    }

    public String getOffset(){
        String path;
        if(this.typeOperator.equals("struct")){
            path = this.symbol.position+"+";
        }else{
            if(this.typeOperator.equals("array")){
                path = this.symbol.position+"+"+this.offset+","+this.symbol.width;
            }else
                path = this.symbol.position+","+this.symbol.width;
        }
        return path;
    }

    public String getVal(){
        String path;
        if (temporal == null){
            if(symbol == null){
                return element;
            }else{
                if(this.typeOperator.equals("array")){
                    if(symbol.base.matches("Program_(.)*"))
                        path = symbol.base+","+"0"+"+"+this.offset+","+symbol.width;
                    else
                        path = symbol.base+","+symbol.position+"+"+this.offset+","+symbol.width;
                }else{
                    if(this.typeOperator.equals("struct")){
                        System.out.println("struct-------------------------------------------------------");
                        path = symbol.base+","+this.children.get(0).getOffset();
                    }
                    else{
                        if(symbol.base.matches("Program_(.)*"))
                            path = symbol.base+","+"0"+","+symbol.width;
                        else
                            path = symbol.base+","+symbol.position+","+symbol.width;
                    }
                }
                return path;
            }
        }else{
            return temporal.getName();
        }
    }

    public Label getTrueLabel(){
        return this.labelTrue;
    }

    public Label getFalseLabel(){
        return this.labelFalse;
    }

    public Label getNextLabel(){
        return this.labelNext;
    }

    public Temporal getTemporal(){
        return this.temporal;
    }

    public String getElement(){
        return this.element;
    }

    public ThreeDirectionInstruction getCode(){
        //String s = "";
        ThreeDirectionInstruction s = new ThreeDirectionInstruction();
        if(this.temporal == null){
            if(this.children.size() > 0 || this.typeOperator.equals("function")){
                if(this.typeOperator.equals("aritop")){
                    temporal = Temporal.getTemporal();
                    for(int i = 0; i < this.children.size(); i++){
                        //s = s + this.children.get(i).getCode();
                        s.addInstruction(this.children.get(i).getCode());
                    }
                    if(this.children.size() > 1)
                        //s = s + temporal.getName()+" = "+this.children.get(0).getVal()+" "+this.element+" "+ this.children.get(1).getVal();
                        s.addInstruction(temporal.getName()+" = "+this.children.get(0).getVal()+" "+this.element+" "+ this.children.get(1).getVal(), "aritopBinary");
                    else
                        //s = s + this.element+" "+this.children.get(0).getVal();
                        s.addInstruction(this.element+" "+this.children.get(0).getVal(), "aritopUnary");
                }else{
                    if(this.typeOperator.equals("relop")){
                        temporal = Temporal.getCMP();
                        for(int i = 0; i < this.children.size(); i++){
                            //s = s + this.children.get(i).getCode();
                            s.addInstruction(this.children.get(i).getCode());
                        }
                        //s = s + temporal.getName()+" = "+this.children.get(0).getVal()+" - "+this.children.get(1).getVal()+"\n";
                        s.addInstruction(temporal.getName()+" = "+this.children.get(0).getVal()+" - "+this.children.get(1).getVal(), "relopCMP");
                        
                        if(this.parent == null){
                            if(labelTrue == null && labelFalse == null && labelNext == null){
                                labelTrue = new Label();
                                labelFalse = new Label();
                                labelNext = new Label();
                            }
                            //s = s + "if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName()+"\n";
                            s.addInstruction("if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName(), "relopIf");
                            //s = s +labelFalse.getName()+":\n"+temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":";
                            //s.addInstruction(labelFalse.getName()+":\n"+temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":","relopRest");
                            s.addInstruction(labelFalse.getName()+":","label");
                            s.addInstruction(temporal.getName()+" = "+"0","cmpAssign0");
                            s.addInstruction("goto "+labelNext.getName(),"gotoInconditional");
                            s.addInstruction(labelTrue.getName()+":","label");
                            s.addInstruction(temporal.getName()+" = 1","cmpAssing1");
                            s.addInstruction(labelNext.getName()+":","label");

                        }else{
                            labelTrue = this.parent.labelTrue;
                            labelFalse = this.parent.labelFalse;
                            if(this.superParent == null){
                                labelNext = this.parent.labelNext;
                                if (this.parent.element.equals("||")){
                                    if(labelNext == null || this.parent.parent == null)
                                        //s = s + "if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName();
                                        s.addInstruction("if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName(), "relopIf");
                                    else
                                        //s = s + "if "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName();
                                        s.addInstruction("if "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName(), "relopIf");
                                }
                                if(this.parent.element.equals("&&")){
                                    if(labelNext == null || this.parent.parent == null)
                                        //s = s + "ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelFalse.getName();
                                        s.addInstruction("ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelFalse.getName(), "relopIfFalse");
                                    else
                                        //s = s + "ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName();
                                        s.addInstruction("ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName(), "relopIfFalse");
                                }
                                if(this.parent.element.equals("!")){
                                    //s = s + "if"+temporal.getName()+" "+this.element+" 0 goto "+labelFalse.getName();
                                    s.addInstruction("if"+temporal.getName()+" "+this.element+" 0 goto "+labelFalse.getName(), "relopIf");
                                }
                            }else{
                                labelNext = this.superParent.labelNext;
                                if (this.superParent.element.equals("||")){
                                    if(labelNext == null || this.superParent.parent == null)
                                        //s = s + "if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName();
                                        s.addInstruction("if "+temporal.getName()+" "+this.element+" 0 goto "+ labelTrue.getName(), "relopIf");
                                    else
                                        //s = s + "if "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName();
                                        s.addInstruction("if "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName(), "relopIf");
                                }
                                if(this.superParent.element.equals("&&")){
                                    if(labelNext == null || this.superParent.parent == null)
                                        //s = s + "ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelFalse.getName();
                                        s.addInstruction("ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelFalse.getName(), "relopIfFalse");
                                    else
                                        //s = s + "ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName();
                                        s.addInstruction("ifFalse "+temporal.getName()+" "+this.element+" 0 goto "+ labelNext.getName(), "relopIfFalse");
                                }
                                if(this.superParent.element.equals("!")){
                                    //s = s + "if"+temporal.getName()+" "+this.element+" 0 goto "+labelFalse.getName();
                                    s.addInstruction("if"+temporal.getName()+" "+this.element+" 0 goto "+labelFalse.getName(), "relopIf");
                                }
                            }
                        }
                    }else{
                        if(this.typeOperator.equals("logicop")){
                            temporal = Temporal.getCMP();
                            if(this.parent == null){
                                if(labelTrue == null && labelFalse == null && labelNext == null){
                                    labelTrue = new Label();
                                    labelFalse = new Label();
                                    labelNext = new Label();
                                }
                            }else{
                                if(this.superParent == null){
                                    labelTrue = this.parent.labelTrue;
                                    labelFalse = this.parent.labelFalse;
                                }else{
                                    labelTrue = this.superParent.labelTrue;
                                    labelFalse = this.superParent.labelFalse;
                                }
                            }
                            for(int i = 0; i < this.children.size(); i++){
                                //s = s + this.children.get(i).getCode();
                                s.addInstruction(this.children.get(i).getCode());
                            }
                            if(this.labelNext != null && this.parent != null){
                                //s = s + this.labelNext.getName()+"\n";
                                s.addInstruction(this.labelNext.getName()+":", "label");
                            }
                            if(this.parent == null){
                                if(this.element.equals("||")){
                                    //s = s +labelFalse.getName()+":\n"+ temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":";
                                    //s.addInstruction(labelFalse.getName()+":\n"+ temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":", "logicopRest");
                                    s.addInstruction(labelFalse.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = "+"0","cmpAssign0");
                                    s.addInstruction("goto "+labelNext.getName(),"gotoInconditional");
                                    s.addInstruction(labelTrue.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = 1","cmpAssing1");
                                    s.addInstruction(labelNext.getName()+":","label");
                                }
                                if (this.element.equals("&&")){
                                    //s = s +labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\ngoto "+labelNext.getName()+"\n"+labelFalse.getName()+":\n"+temporal.getName()+" = "+"0\n"+labelNext.getName()+":";
                                    //s.addInstruction(labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\ngoto "+labelNext.getName()+"\n"+labelFalse.getName()+":\n"+temporal.getName()+" = "+"0\n"+labelNext.getName()+":", "logicopRest");
                                    s.addInstruction(labelTrue.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = "+"1","cmpAssign1");
                                    s.addInstruction("goto "+labelNext.getName(),"gotoInconditional");
                                    s.addInstruction(labelFalse.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = 0","cmpAssing0");
                                    s.addInstruction(labelNext.getName()+":","label");
                                }
                                if (this.element.equals("!")){
                                    //s = s +labelFalse.getName()+":\n"+ temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":";
                                    //s.addInstruction(labelFalse.getName()+":\n"+ temporal.getName()+" = "+"0\ngoto "+labelNext.getName()+"\n"+labelTrue.getName()+":\n"+temporal.getName()+" = "+"1\n"+labelNext.getName()+":", "logicopRest");
                                    s.addInstruction(labelFalse.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = "+"0","cmpAssign0");
                                    s.addInstruction("goto "+labelNext.getName(),"gotoInconditional");
                                    s.addInstruction(labelTrue.getName()+":","label");
                                    s.addInstruction(temporal.getName()+" = 1","cmpAssing1");
                                    s.addInstruction(labelNext.getName()+":","label");
                                }
                            }
                        }else{
                            if(this.typeOperator.equals("function")){
                                temporal = Temporal.getTemporal();
                                for(int i = 0; i < this.children.size(); i++){
                                    //s = s + this.children.get(i).getCode();
                                    s.addInstruction(this.children.get(i).getCode());
                                }
                                s.addInstruction("","functionStart");
                                for(int i = 0; i < this.children.size(); i++){
                                    //s = s + "param "+ this.children.get(i).getVal()+"\n";
                                    s.addInstruction("param "+ this.children.get(i).getVal(),"functionParam");
                                }
                                if(this.children.size() > 0)
                                    //s = s + temporal.getName() + " = "+"call "+this.element+", "+this.children.size();
                                    s.addInstruction(temporal.getName() + " = "+"call "+this.element+"_"+this.children.size(), "functionCall");
                                else
                                    //s = s + temporal.getName() + " = "+"call "+this.element+", 0";
                                    s.addInstruction(temporal.getName() + " = "+"call "+this.element+"_0", "functionCall");
                                
                            }else{
                                if(this.typeOperator.equals("array")){
                                    //s = s + this.children.get(0).getCode();
                                    s.addInstruction(this.children.get(0).getCode());
                                    this.offset = this.children.get(0).getVal()+"*"+symbol.width;
                                    this.element = this.getVal()+"["+this.offset+"]";
                                }else{
                                    if(this.typeOperator.equals("struct")){
                                        //s = s + this.children.get(0).getCode();
                                        s.addInstruction(this.children.get(0).getCode());
                                    }
                                }
                            }
                        }
                    }
                }
            }else{
                /*if(this.typeOperator.equals("location")){
                    s = this.getVal();
                }else{
                    if(this.typeOperator.equals("array")){
                        s = this.brother.getCode();
                        s = s + this.getVal()+"["+this.brother.getVal()+"*"+symbol.width+"]";
                    }else{
                        if(this.typeOperator.equals("value")){
                            s = this.getVal();
                        }
                    }
                }*/
                return null;
            }
        }else{
            return null;
        }
        return s;
    }

    public void recicleTemporal(){
        if(temporal != null && !temporal.getName().equals("tcmp"))
            temporal.recicle();
        for(int i = 0; i < this.children.size(); i++){
            this.children.get(i).recicleTemporal();
        }
    }

    public String toString(){
        String s = "{"+this.element;
        for(int i = 0; i < this.children.size(); i++){
            s = s +",["+this.children.get(i) +"]";
        }
        s = s + "}";
        return s;
    }
}
